home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Die Speccy' 97
/
Die Speccy' 97.iso
/
amiga_system
/
the_aminet
/
util
/
boot
/
bind_namesii.lha
/
BindNamesII.c
< prev
next >
Wrap
C/C++ Source or Header
|
1995-09-14
|
22KB
|
1,027 lines
/* $Header: Src/rcs/BindNamesII.c,v 1.5 1995/06/21 17:24:04 cmh Exp cmh $
*
* BindNamesII: Handy assign/path maker.
* Copyright (C) 1994-95 Magnus Holmgren <cmh@augs.se>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <exec/types.h>
#include <exec/lists.h>
#include <exec/memory.h>
#include <dos/exall.h>
#include <dos/dosextens.h>
#include <clib/alib_protos.h>
#include <clib/dos_protos.h>
#include <clib/exec_protos.h>
#include <clib/utility_protos.h>
#ifdef __GNUC__
#include <inline/alib.h>
#include <inline/dos.h>
#include <inline/exec.h>
#include <inline/utility.h>
#endif
#include <dos/dos.h>
#include <dos/dostags.h>
#include <dos/rdargs.h>
#include <strings.h>
#include "assignnode.h"
#define Prototype extern
#include "proto.h"
#include "macros.h"
/* Program arguments */
struct Flags
{
STRPTR Drawer;
LONG Test;
LONG Verbose;
LONG Quiet;
STRPTR PathAddChar;
STRPTR PathStart;
};
#define TEMPLATE "DRAWER,TEST/S,VERBOSE/S,QUIET/S,PATHADDCHAR/K,PATHSTART/K"
/* Template for an assign in a file */
struct AssignTemplate
{
STRPTR Name;
STRPTR *Target;
STRPTR Alias;
LONG *Pri;
LONG Defer;
LONG Path;
LONG Add;
LONG PathAdd;
};
#define ASSIGNTEMPLATE "NAME/A,TARGET/A/M,ALIAS/K,PRI=PRIORITY/K/N,DEFER/S,PATH/S,ADD/S,PATHADD/S"
/* Size of read buffer. Lines larger than this are truncated. */
#define READBUF_SIZE 512
#ifdef __GNUC__
BYTE __nocommandline = 1; /* Disable libnix argument parsing */
LONG __osversion = 37;
#endif
/* Global variables: */
/* If Test is TRUE, then don't do any assigns/mounts/createdirs/pathadds.
* Assigns (to "known" volumes), createdirs and pathadds are assumed to
* succeed, while mounts are assumed to fail (since all unresolved
* volumes are mounted before BindNamesII gives up).
*/
Prototype BOOL Test;
BOOL Test;
/* Don't print the list over failed assigns. */
BOOL Quiet;
/* If TRUE, then output extra "debug" information. */
BOOL Verbose;
/* Path separator used by the PATHADD option */
Prototype TEXT PathAddChar;
TEXT PathAddChar = '|';
/* Name of PathHandler device */
Prototype STRPTR PathStart;
STRPTR PathStart = "Path:";
/* Length of above string */
LONG PathStartLen = 5;
/* End of global variables. */
#ifdef __GNUC__
STRPTR
stpcpy( STRPTR dest, STRPTR source )
{
while( ( *dest++ = *source++ ) );
return( dest - 1 );
}
#endif
/* Read the named file, and add the AssignNodes it contains to the list. */
BOOL
ReadAssignFile( STRPTR name, struct MinList *list )
{
struct RDArgs *rdArg;
BOOL ret = FALSE;
SetIoErr( 0 );
if( ( rdArg = AllocDosObject( DOS_RDARGS, NULL ) ) )
{
BPTR file;
TEXT readBuffer[ READBUF_SIZE + 2 ];
rdArg->RDA_Flags = RDAF_NOPROMPT;
if( ( file = Open( name, MODE_OLDFILE ) ) )
{
struct AssignTemplate flags;
struct AssignNode *allocOk = ( struct AssignNode * ) 4;
LONG line = 0;
ret = TRUE;
while( allocOk && FGets( file, readBuffer, READBUF_SIZE ) )
{
++line;
/* Skip any comments */
if( *readBuffer == ';' || *readBuffer == '#' )
{
continue;
}
/* Prepare for parsing */
rdArg->RDA_Source.CS_Buffer = readBuffer;
rdArg->RDA_Source.CS_Length = strlen( readBuffer );
rdArg->RDA_Source.CS_CurChr = 0;
bzero( &flags, sizeof( flags ) );
if( ReadArgs( ASSIGNTEMPLATE, ( LONG * ) &flags, rdArg ) )
{
WORD type = ASN_NORMAL, flag = 0;
BYTE pri = 0;
if( !Stricmp( flags.Name, "CMDPATH" ) )
{
type = ASN_CMDPATH;
}
else if( flags.Path )
{
type = ASN_PATH;
}
else if( flags.Defer )
{
type = ASN_DEFER;
}
if( flags.Add )
{
flag |= ANF_ADD;
}
if( flags.PathAdd )
{
flag |= ANF_PATHADD;
}
if( flags.Pri )
{
pri = *flags.Pri;
if( ( *flags.Pri > 127 ) || ( *flags.Pri < -128 ) )
{
Printf( "Prority out of range on line %ld in file \"%s\"\n",
line, name );
ret = FALSE;
}
}
if( ( type != ASN_CMDPATH ) && !strchr( flags.Name, ':' ) )
{
Printf( "Colon missing on line %ld in file \"%s\"\n",
line, name );
ret = FALSE;
}
allocOk = AllocAssignNode(
list,
flags.Name,
flags.Target,
pri,
flags.Alias,
type,
flag );
FreeArgs( rdArg );
}
else
{
PrintError( "Error in line %ld in file \"%s\"", line, name );
ret = FALSE;
}
}
if( !allocOk )
{
SetIoErr( ERROR_NO_FREE_STORE );
}
if( IoErr() )
{
PrintError( "Error reading line %ld in file \"%s\"", line, name );
ret = FALSE;
}
Close( file );
}
else
{
PrintError( "Couldn't open file \"%s\"", name );
}
FreeDosObject( DOS_RDARGS, rdArg );
}
else
{
PrintNoMem();
}
return( ret );
}
/* Placed here to save some space. A string merge option in DICE would've been nice.. :) */
const TEXT NoExamine_Msg[] = "Couldn't examine drawer \"%s\"";
/* Scan the specified drawer, and call ReadAssignFile() for each file encountered.
* I haven't bothered to use ExAll() here. We're probably dealing with relatively
* few files in the drawer (usually less than a dozen), so the code to handle the
* ExAll() stuff simply isn't worth it. We keep it small instead.
*/
BOOL
ScanDrawer( STRPTR drawer, struct MinList *list )
{
struct FileInfoBlock *fib;
LONG err = TRUE;
if( ( fib = AllocDosObject( DOS_FIB, NULL ) ) )
{
BPTR lock;
if( ( lock = Lock( drawer, SHARED_LOCK ) ) )
{
BPTR oldLock = CurrentDir( lock );
if( ( Examine( lock, fib ) ) )
{
/* Make sure we've examined a drawer */
if( fib->fib_DirEntryType > 0 )
{
err = FALSE;
while( ExNext( lock, fib ) )
{
/* Only process files */
if( fib->fib_DirEntryType < 0 )
{
/* Skip any symbols... */
if( Stricmp( fib->fib_FileName + strlen( fib->fib_FileName ) - 5, ".info" ) )
{
if( !ReadAssignFile( fib->fib_FileName, list ) )
{
err = TRUE;
break;
}
}
}
}
if( !err && ( IoErr() != ERROR_NO_MORE_ENTRIES ) )
{
PrintError( NoExamine_Msg, drawer );
err = TRUE;
}
}
else
{
Printf( "BindNamesII: '%s' is not a drawer\n", drawer );
}
}
else
{
PrintError( NoExamine_Msg, drawer );
}
CurrentDir( oldLock );
UnLock( lock );
}
else
{
PrintError( NoExamine_Msg, drawer );
}
FreeDosObject( DOS_FIB, fib );
}
else
{
PrintNoMem();
}
return( !err );
}
/* Check if this nodes parent (or the parents parent, etc.), refer
* to unknown volumes. All nodes that directly refers to unknown
* nodes should have the flag ANF_STRAY set, or this function will not
* behave as it should.. :)
* If a loop is detected, return -1. Otherwise 1 is returned if a stray node
* is found, and 0 for no such nodes.
*/
WORD
IsStray( struct AssignNode *node )
{
WORD ret = 0;
/* Check for loops. We don't want to overflow the stack just because
* the user passed in bad data, do we? :)
*/
if( node->Flags & ANF_VISIT )
{
ret = -1;
node->Flags |= ANF_LOOP;
}
else if( node->Flags & ANF_LOOP ) /* Looped node? */
{
ret = -1;
}
else if( node->Flags & ANF_STRAY ) /* Stray node? */
{
ret = 1;
}
else
{
struct PathNode *pathNode, *next;
/* Indicate that we've visited this node */
node->Flags |= ANF_VISIT;
/* Check each assign in a multiassign */
FOREACHNODE( &node->Path, pathNode, next )
{
/* Do we have a parent that is ANF_STRAY? */
if( pathNode->Parent && ( ret = IsStray( pathNode->Parent ) ) )
{
/* We found out what we wanted, so stop */
break;
}
}
if( ret > 0 )
{
node->Flags |= ANF_STRAY;
}
else if( ret < 0 )
{
/* Loop detected! */
node->Flags |= ANF_LOOP;
}
/* Clear the visit flag. */
node->Flags &= ~ANF_VISIT;
}
return( ret );
}
/* Handle any PATHADDs for the current node. Returns FALSE if it
* couldn't allocate the needed memory to do the "conversion".
*/
BOOL
DoPathAdd( struct AssignNode *node )
{
struct PathNode *pathNode, *next;
STRPTR newName;
LONG count = 0, len = PathStartLen + 2;
BOOL havePath = FALSE, success = TRUE;
/* Get total string size needed, and find out if we have any "Path:".
* Also check number of targets.
*/
FOREACHNODE( &node->Path, pathNode, next )
{
if( !Strnicmp( pathNode->Name, PathStart, PathStartLen ) )
{
havePath = TRUE;
strcpy( pathNode->Name, pathNode->Name + PathStartLen );
}
len += strlen( pathNode->Name );
++count;
}
/* There are paths to be added to the first one. */
if( havePath )
{
if( ( newName = AllocVec( len + count, MEMF_ANY ) ) )
{
struct PathNode *firstPath = ( struct PathNode * )
RemHead( ( struct List * ) &node->Path );
STRPTR work;
/* Copy header to new name */
work = stpcpy( newName, PathStart );
work = stpcpy( work, firstPath->Name );
/* Remove and copy old paths to the new one */
FOREACHNODE( &node->Path, pathNode, next )
{
*work = PathAddChar;
++work;
work = stpcpy( work, pathNode->Name );
/* Free the old PathNode */
FreePathNode( pathNode );
}
FreeVec( firstPath->Name );
firstPath->Name = newName;
/* Reinit list and add new node */
NewList( ( struct List * ) &node->Path );
AddHead( ( struct List * ) &node->Path, ( struct Node * ) firstPath );
}
else
{
success = FALSE;
}
}
return( success );
}
/* This function traverses the AssignList, sets the Flags and Parent fields for all nodes.
* Also handles '/' chars in the beginning of names and PATHADDs.
* This is so that the AssignList() function knows when to try to do an assign.
* Also makes some validity checking.
* Returns FALSE if a PATHADD failed (it might need to reallocate some memory).
*/
BOOL
PrepareAssignList( struct MinList *list )
{
struct AssignNode *node, *next;
LONG count;
BOOL success = TRUE;
/* First pass. Set Parent and Flags (ANF_STRAY) for all nodes.
* Check for multiple targets for ANF_DEFER or ANF_PATH.
* Remove leading '/' chars (moving such nodes to the top of the Path list).
*/
FOREACHNODE( list, node, next )
{
struct PathNode *pathNode, *nextPath;
STRPTR colon;
TEXT oldChar;
/* Handle '/' chars */
{
struct PathNode *firstPath = NULL;
/* Check for '/' chars in the beginning of targets. */
FOREACHNODE( &node->Path, pathNode, nextPath )
{
if( *pathNode->Name == '/' )
{
firstPath = pathNode;
strcpy( pathNode->Name, pathNode->Name + 1 );
}
}
if( firstPath )
{
Remove( ( struct Node * ) firstPath );
AddHead( ( struct List * ) &node->Path, ( struct Node * ) firstPath );
}
}
if( node->Flags & ANF_PATHADD )
{
/* Merge PATHADDs into a single string */
if( !( success = DoPathAdd( node ) ) )
{
break;
}
}
count = 0;
/* Check each assign in a multiassign */
FOREACHNODE( &node->Path, pathNode, nextPath )
{
++count;
if( ( colon = strchr( pathNode->Name, ':' ) ) )
{
++colon;
oldChar = *colon;
*colon = 0;
}
/* Do we have a parent? */
if( ( pathNode->Parent = FindAssignNode( list, pathNode->Name ) ) )
{
node->Flags |= ANF_NODE;
}
/* Does the node refer to a not yet known volume? */
else if( ( node->AssignType == ASN_NORMAL ) &&
!HaveProc( pathNode->Name ) )
{
node->Flags |= ANF_STRAY;
}
if( colon )
{
*colon = oldChar;
}
}
if( ( node->AssignType != ASN_NORMAL ) &&
( node->AssignType != ASN_CMDPATH ) &&
( ( count > 1 ) || ( node->Flags & ANF_ADD ) ) )
{
node->Flags |= ANF_BAD; /* PATH of DEFER assigns can't be multiple. */
}
/* Did we find any unresolved link? */
if( !( node->Flags & ( ANF_NODE | ANF_STRAY | ANF_BAD ) ) )
{
node->Flags |= ANF_VOLUME; /* No */
}
}
/* Second pass. Set ANF_STRAY for nodes whos parent have ANF_STRAY set.
* Also sets ANF_LOOP, if a loop is detected.
*/
FOREACHNODE( list, node, next )
{
IsStray( node );
}
return( success );
}
/* Do the assign for this node. Handles all different cases.
* Creates the needed drawers.
*/
BOOL
AssignNode( struct AssignNode *node )
{
BOOL success = TRUE;
if( !Test )
{
struct PathNode *pathNode, *next;
STRPTR colon;
BOOL assignLock = TRUE; /* If TRUE, call AssignLock() for the first node in Path. */
BPTR target; /* Lock of target drawer */
/* Check if we should add to an existing assign. */
if( ( node->Flags & ANF_ADD ) && HaveProc( node->Name ) )
{
assignLock = FALSE;
}
if( ( node->AssignType == ASN_CMDPATH ) && !( node->Flags & ANF_ADD ) )
{
ClearProcPath();
}
/* Remove any colon in the assign name. */
if( ( colon = strchr( node->Name, ':' ) ) )
{
*colon = 0;
}
FOREACHNODE( &node->Path, pathNode, next )
{
switch( node->AssignType )
{
case ASN_PATH:
success = AssignPath( node->Name, pathNode->Name );
break;
case ASN_DEFER:
success = AssignLate( node->Name, pathNode->Name );
break;
case ASN_CMDPATH:
/* Try to create the target */
CreateDirPath( pathNode->Name );
success = AddNameProcPath( pathNode->Name );
break;
default:
success = FALSE;
/* Try to create the target (may fail for e.g. path assings) */
CreateDirPath( pathNode->Name );
/* And lock the target... */
if( ( target = Lock( pathNode->Name, SHARED_LOCK ) ) )
{
/* Fire! :) */
if( assignLock )
{
success = AssignLock( node->Name, target );
assignLock = FALSE;
}
else
{
success = AssignAdd( node->Name, target );
}
/* A miss. Unlock target again.. :) */
if( !success )
{
UnLock( target );
}
}
break;
}
if( !success )
{
node->Flags |= ANF_FAILED;
node->IoErr = IoErr();
break; /* Terminate loop */
}
}
if( colon )
{
*colon = ':';
}
}
if( success )
{
node->Flags |= ANF_ASSIGNED;
if( !Test && ( node->AssignType != ASN_CMDPATH ) && node->Alias )
{
SetComment( node->Name, node->Alias );
}
}
return( success );
}
/* Again some strings to save space... */
const TEXT Mount_Msg[] = "C:Mount %s\n";
const TEXT Nil_Msg[] = "Nil:";
/* Mount the device found in name. */
LONG
MountAssign( STRPTR name )
{
LONG success = 0;
if( !Test )
{
/* Check if this volume already have been mounted */
if( !HaveProc( name ) )
{
STRPTR command, colon;
TEXT oldChar;
success = -1;
/* Remove anything after the colon */
if( ( colon = strchr( name, ':' ) ) )
{
++colon;
oldChar = *colon;
*colon = 0;
}
if( ( command = AllocVec( sizeof( Mount_Msg ) + strlen( name ), MEMF_ANY ) ) )
{
BPTR in;
RawDoFmt( Mount_Msg, &name, ( VOID (*) ) "\x16" "\xc0" "\x4e" "\x75", command );
if( ( in = Open( Nil_Msg, MODE_OLDFILE ) ) )
{
BPTR out;
if( ( out = Open( Nil_Msg, MODE_OLDFILE ) ) )
{
if( Verbose )
{
Printf( "BindNamesII: Mounting volume \"%s\"\n", ( LONG ) name );
}
success = SystemTags( command,
SYS_Input, in,
SYS_Output, out,
TAG_DONE );
Close( out );
}
Close( in );
}
FreeVec( command );
}
if( colon )
{
*colon = oldChar;
}
}
}
return( success );
}
/* Handle the assign for one node. This includes checking if a node
* have unassigned parents, in which case that assign is delayed.
* The assign is placed on the proper list.
*/
VOID
HandleAssign(
struct MinList *list,
struct MinList *assigned,
struct MinList *failed,
struct AssignNode *node )
{
struct PathNode *pathNode, *next;
BOOL asn = TRUE, fail = FALSE;
/* Check if all parents for this node have been assigned. */
FOREACHNODE( &( node->Path ), pathNode, next )
{
/* Do we have a parent? */
if( pathNode->Parent )
{
/* Yes, check if it is assigned already */
if( !( pathNode->Parent->Flags & ANF_ASSIGNED ) )
{
asn = FALSE;
}
/* Did the assing fail (second check mostly for speed.. :)? */
if( ( pathNode->Parent->Flags & ANF_FAILED ) ||
( pathNode->Parent->Flags & ANF_BADPARENT ) )
{
fail = TRUE;
node->Flags |= ANF_BADPARENT;
}
}
}
/* If the parents have been assigned, then assign this one too. */
if( asn )
{
if( AssignNode( node ) )
{
AddTail( ( struct List * ) assigned, ( struct Node * ) node );
}
else
{
AddTail( ( struct List * ) failed, ( struct Node * ) node );
}
}
else
{
if( fail )
{
/* A parent failed, so this must fail too */
AddTail( ( struct List * ) failed, ( struct Node * ) node );
}
else
{
/* Retry this node later on */
AddTail( ( struct List * ) list, ( struct Node * ) node );
}
}
}
/* Go trough the list and do all assigns in the proper order.
* Returns FALSE if some assigns failed.
* NOTE: The list is freed by this function!
*/
BOOL
AssignList( struct MinList *list )
{
struct AssignNode *node;
struct MinList assigned; /* Holds the assigned volumes */
struct MinList failed; /* Holds the assigns that failed */
struct MinList stray; /* Temporary list for stray nodes. */
BOOL success = TRUE;
/* Init all lists. */
NewList( ( struct List * ) &assigned );
NewList( ( struct List * ) &failed );
NewList( ( struct List * ) &stray );
/* First pass. Do all assigns to existing volumes,
* or to other assigns that refers to existing volumes (etc).
* Move stray and path nodes to the proper list for later processing.
*/
while( ( node = ( struct AssignNode * ) RemHead( ( struct List * ) list ) ) )
{
if( node->Flags & ANF_STRAY )
{
/* These needs mounting, handled later */
AddTail( ( struct List * ) &stray, ( struct Node * ) node );
}
else if( node->Flags & ( ANF_LOOP | ANF_BAD ) )
{
/* Remove known bad nodes */
AddTail( ( struct List * ) &failed, ( struct Node * ) node );
}
else if( node->Flags & ANF_VOLUME )
{
/* This volume should be assignable */
if( AssignNode( node ) )
{
AddTail( ( struct List * ) &assigned, ( struct Node * ) node );
}
else
{
AddTail( ( struct List * ) &failed, ( struct Node * ) node );
}
}
else
{
/* This node have other node(s) as parent(s) */
HandleAssign( list, &assigned, &failed, node );
}
}
/* Second pass. Try to mount all volumes that needs it. */
while( ( node = ( struct AssignNode * ) RemHead( ( struct List * ) &stray ) ) )
{
struct PathNode *pathNode, *next;
FOREACHNODE( &node->Path, pathNode, next )
{
if( !pathNode->Parent )
{
/* Try to mount the volume it refers to. Ignore any errors,
* since the mount might take a little while. Thus, the
* "dupe mount checking" in MountAssign() might not notice
* a previous Mount. Or so I suspect.. :)
* I don't know enough about Mount internals... :/
*/
MountAssign( pathNode->Name );
}
}
AddTail( ( struct List * ) list, ( struct Node * ) node );
}
/* Third pass. Do the rest of the assigns, those that needed a
* mount first. No stray nodes to handle, all "bad" nodes are
* gone. We still need to check for unassigned parents though,
*/
while( ( node = ( struct AssignNode * ) RemHead( ( struct List * ) list ) ) )
{
HandleAssign( list, &assigned, &failed, node );
}
if( Verbose )
{
if( IsListEmpty( ( struct List * ) &assigned ) )
{
Printf( "BindNamesII:\nNo assigns made\n" );
}
else
{
Printf( "BindNamesII:\nAssigns:\n" );
PrintAssignList( &assigned );
}
}
if( !( IsListEmpty( ( struct List * ) &failed ) || Quiet ) )
{
if( !Verbose )
{
Printf( "BindNamesII:" );
}
Printf( "\nUnresolved assigns:\n" );
PrintAssignList( &failed );
success = FALSE;
}
FreeAssignList( &assigned );
FreeAssignList( &failed );
return( success );
}
#ifdef __GNUC__
int main( VOID )
#else
LONG _main( VOID )
#endif
{
struct RDArgs *rdArg;
struct Flags flags;
struct Window *window;
struct Process *thisTask;
LONG code = RETURN_FAIL; /* A bit pessimistic perhaps, but it saves some code! :) */
/* Disable volume requesters */
thisTask = ( struct Process * ) FindTask( NULL );
window = thisTask->pr_WindowPtr;
thisTask->pr_WindowPtr = ( struct Window * ) -1;
bzero( &flags, sizeof( flags ) );
flags.Drawer = "Sys:Names";
if( ( rdArg = ReadArgs( TEMPLATE, ( LONG * ) &flags, NULL ) ) )
{
struct MinList assignList;
NewList( ( struct List * ) &assignList );
if( flags.Test )
{
Test = TRUE;
}
if( flags.Verbose )
{
Verbose = TRUE;
}
if( flags.Quiet )
{
Quiet = TRUE;
}
if( flags.PathAddChar )
{
PathAddChar = *flags.PathAddChar;
}
if( flags.PathStart )
{
PathStart = flags.PathStart;
PathStartLen = strlen( PathStart );
}
if( ScanDrawer( flags.Drawer, &assignList ) )
{
if( !PrepareAssignList( &assignList ) )
{
PrintNoMem();
}
else
{
if( AssignList( &assignList ) )
{
code = RETURN_OK;
}
else
{
code = RETURN_WARN;
}
}
}
else
{
FreeAssignList( &assignList );
}
FreeArgs( rdArg );
}
else
{
PrintError( "Error in arguments" );
}
thisTask->pr_WindowPtr = window;
return( code );
}